import configparser
import os
import sys
import inspect  # 去除奇怪的缩进用的
import re
from concurrent.futures import ThreadPoolExecutor, ALL_COMPLETED, wait
import shutil
import time


def singleton(cls):
    """单例装饰器"""
    _instance = {}

    def _singleton(*args, **kwargs):
        # 先判断这个类有没有对象
        if cls not in _instance:
            _instance[cls] = cls(*args, **kwargs)  # 创建一个对象,并保存到字典当中
        # 将实例对象返回
        return _instance[cls]

    return _singleton


@singleton
class Main_manager(object):
    Formats = dict()
    Paths = dict()

    def __init__(self, file_list):
        self._config = self._load_config()
        self._file_list = file_list
        # self._cur_path = os.getcwd()
        self.file_obj_list = []
        self.Paths = dict(dict(self._config)["Paths"])
        self.Formats = dict(dict(self._config)["Formats"])
        self.dict1 = {
            "Others": self.Input_file,
            "docs": self.Doc_file,
            "videos": self.Video_file,
        }

    def _load_config(self):
        """读取配置(config.ini)，返回一个配置对象，可以被转化为字典(dict),
        若配置文件不存在，则可选生成它"""
        file_list = os.listdir()
        try:
            if "config.ini" not in file_list:
                raise FileNotFoundError
        except FileNotFoundError:
            input_choice = input("配置文件不存在，是否开始生成(y/n):")
            if input_choice.lower() == "y" or input_choice.lower() == "":
                print("开始创建配置文件......")
                self._create_config()
                return self._load_config()
            else:
                sys.exit()
        config = configparser.ConfigParser()
        config.read("config.ini", encoding="utf-8")
        print("读取配置文件成功")
        return config

    def _create_config(self):
        """创建一个默认的配置文件(config.ini)"""
        desktop_path = os.path.join(os.path.expanduser("~"), "Desktop")
        try:
            with open("config.ini", mode="w", encoding="utf8") as config_file:
                default_conf = fr"""
                # 这是一个由程序生成的配置文件，请不要删除
                # 井号开头的行是注释，往往代表下一行的作用
                # 您也可以通过修改等号右边的信息来改变默认路径
                [Paths]
                # 视频剪辑存档
                videos=D:\视频剪辑存档\临时素材
                # 临时文件
                tmp_file=D:\临时文件
                # 桌面
                desktop={desktop_path}
                docs = D:\临时文件\文档
                Others = D:\临时文件\其它

                [Formats]
                # 这里储存了所有的视频格式
                videos = [".mp4",".mov",".mkv",".avi"]
                # 这里储存了所有的文档格式
                docs = [".docx",".doc",".pptx",".ppt",".xls",".xlsx",".md"]
                """

                # 清除缩进引起的多个空格
                config_file.write(inspect.cleandoc(default_conf))
                print("创建配置文件成功")
        except FileNotFoundError:
            input("失败，按回车关闭程序")
            sys.exit()

    def _name_2_obj(self, file_name):
        """通过文件后缀判断文件类别，产生一个对应的文件对象"""
        file_type = re.findall(r"\.[^\.]*$", file_name, flags=re.I)[-1]
        for i, j in self.Formats.items():
            if file_type in j:
                file_collection = i
                break
        else:
            file_collection = "Others"

        return self.dict1[file_collection](self, file_name)

    def start(self):
        for i in self._file_list:
            self.file_obj_list.append(self._name_2_obj(i))
        if input("确定移动请输入Yes:") == "Yes":
            with ThreadPoolExecutor(max_workers=5) as t:
                all_task = [t.submit(i.move()) for i in self.file_obj_list]
                wait(all_task, timeout=None, return_when=ALL_COMPLETED)
                print("\n" + "=" * 10)
                print("处理完毕")
        else:
            sys.exit()

    class Input_file(object):
        """所有需要被移动文件的基类，提供move()的方法，作用是获取文件目标位置，移动并且记录移动信息，最后在控制台报告"""

        def __init__(self, outer, file_name=None):
            self._file_name = file_name
            self.outer = outer

        def _get_destination(self):
            """得到目标位置"""
            return self.outer.Paths["others"]

        def move(self):
            """移动到目标位置"""
            destination = self._get_destination()
            try:
                shutil.copy2(self._file_name, self._get_destination())
            except FileNotFoundError:
                print(f"{self._file_name}文件不存在，退出")
                return
            finally:
                self._log(destination)

        def _log(self, destination):
            """留下记录文本"""
            cur_time = time.asctime( time.localtime(time.time()) )
            movement_log = f"{cur_time} {self._file_name} --> {destination}"
            print(movement_log)
            with open("log.txt", mode="a+", encoding="utf-8") as log_file:
                log_file.write(movement_log+"\n")

    class Doc_file(Input_file):
        def _get_destination(self):
            """得到目标位置"""
            return self.outer.Paths["docs"]

    class Video_file(Input_file):
        def _get_destination(self):
            """得到目标位置"""
            return self.outer.Paths["videos"]


if __name__ == "__main__":
    main_manager = Main_manager(sys.argv[1:])
    main_manager.start()
